/*
 * MIT License
 * 
 * Copyright (c) 2017 wen.gu <454727014@qq.com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*****************************************************************************
 * Name:simple_unit_test.h
 *
 * Purpose: simple unit test framework
 *
 * Developer:
       wen.gu,2015-12-29
 *
 *****************************************************************************/

#ifndef __SIMPLE_UNIT_TEST_H__
#define __SIMPLE_UNIT_TEST_H__

#include <iostream>
#include <string>
#include <vector>
using namespace std;
/***************************************************************************
 *
 * macro declaration
 *
 ***************************************************************************/

//unit test return code
#define UT_OK (0)
#define UT_FAIL (-1)
#define UT_FATAL (-2)

#if defined(_WIN32) || defined(_WIN64)
#define SU_WINDOWS
typedef __int64 BIGGEST_INT;
#elif defined(__linux__)
#define SU_LINUX
typedef long long BIGGEST_INT;
#endif

/***************************************************************************
 *
 * data struct declaration
 *
 ***************************************************************************/
namespace simpleUnitTest{

class Test
{
public:
    Test(const char* testName);

public:
    virtual ~Test();
public:
    virtual void testBody() = 0;
    const char* getTestName() const;
    int getTestResult() const;
    void setTestResult(int result);
protected:
    int mTestResult;
    string mTestName;    
};

class TestCase
{
public:
    TestCase(const char* caseName);
    virtual ~TestCase();
public:
    //success:UT_OK,else failed
    int runCase();

    const char* getCaseName() const;

    Test* getCurrentTest() const;

    Test* addTest(Test* test);
    
    const vector<Test*>& getTestList() const;
    size_t getTestCount();
    size_t getPassedCount();
    size_t getFailedCount();

protected:
    size_t mPassedCnt;
    size_t mFailedCnt;
private:
    Test* mCurrentTest;
    vector<Test*> mTests;
    string mCaseName;
};

class UnitTest
{
public:
    static UnitTest* getInstance();

public:
    Test* registerTest(const char* caseName, Test* item);
    int runUnitTest();

    const TestCase* getCurrentTestCase() const;

private:
    TestCase* findTestCaseWithName(const char* caseName);
    size_t getTotalTestCount();
    void outputPassedLog();
    void outputFailedLog(vector<TestCase*>& failedTestCases);
private:
    UnitTest();
    ~UnitTest();
private:    
    int mPassedCnt; //passed case count
    int mFailedCnt; //failed case count
    size_t mTotalTestCnt;
    TestCase* mCurrentTestCase;
    vector<TestCase*> mTestCasees; //for testcase
    //vector<TestCase*> mTests; //for all test
};


/***************************************************************************
 * compare helper template define
 ***************************************************************************/
// A macro for implementing the helper functions needed to implement
// ASSERT_?? and EXPECT_??.  It is here just to avoid copy-and-paste
// of similar code.
//
// For each templatized helper function, we also define an overloaded
// version for BiggestInt in order to reduce code bloat and allow
// anonymous enums to be used with {ASSERT|EXPECT}_?? when compiled
// with gcc 4.
//
// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
#define STEST_IMPL_CMP_HELPER_(op_name, op)\
template <typename T1, typename T2>\
bool CmpHelper##op_name(const char* expr1, const char* expr2, int error,\
                        const char* fileName, int line, const T1& val1, const T2& val2) {\
  if (val1 op val2) {\
    return true;\
  } else {\
    ::simpleUnitTest::UnitTest::getInstance()->getCurrentTestCase()->getCurrentTest()->setTestResult(error);\
    cout<<"["<<fileName<<","<<line<<"]"<<"Error:"<<endl;\
    cout<<"Expected:("<< expr1 <<") " #op " ("<< expr2 <<")"<<endl;\
    cout<<"Actual:"<< val1 << " vs " << val2 <<endl;\
  }\
  return false;\
}\
bool CmpHelper##op_name(const char* expr1, const char* expr2, int error, \
    const char* fileName, int line, const BIGGEST_INT val1, const BIGGEST_INT val2);


// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
// Implements the helper function for {ASSERT|EXPECT}_EQ
STEST_IMPL_CMP_HELPER_(EQ, ==);
// Implements the helper function for {ASSERT|EXPECT}_NE
STEST_IMPL_CMP_HELPER_(NE, !=);
// Implements the helper function for {ASSERT|EXPECT}_LE
STEST_IMPL_CMP_HELPER_(LE, <=);
// Implements the helper function for {ASSERT|EXPECT}_LT
STEST_IMPL_CMP_HELPER_(LT, <);
// Implements the helper function for {ASSERT|EXPECT}_GE
STEST_IMPL_CMP_HELPER_(GE, >=);
// Implements the helper function for {ASSERT|EXPECT}_GT
STEST_IMPL_CMP_HELPER_(GT, >);

class AssertionResult 
{
public:  
    // Copy constructor.
    // Used in EXPECT_TRUE/FALSE(assertion_result).
    AssertionResult(const AssertionResult& other);
    // Used in the EXPECT_TRUE/FALSE(bool_expression).
    explicit AssertionResult(bool success) : mSuccess(success) {}

    // Returns true iff the assertion succeeded.
    operator bool() const { return mSuccess; }  // NOLINT

    // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE.
    AssertionResult operator!() const
    {
        AssertionResult negation(!mSuccess);
        return negation;
    }
private:
    bool mSuccess;
    void operator= (AssertionResult &);
    
};

}
/***************************************************************************
 *
 * API declaration
 *
 ***************************************************************************/

//test case macro define
#define TESTCASE_NAME(test_case_name, test_name) test_case_name##test_name##_TEST
    
#define SIMPLE_TEST__(test_case_name, test_name)\
    class TESTCASE_NAME(test_case_name, test_name): public ::simpleUnitTest::Test\
    {\
    public:\
        TESTCASE_NAME(test_case_name, test_name)(const char* testName)\
        :Test(testName){}\
        virtual void testBody();\
    private:\
        static ::simpleUnitTest::Test* const mTest;\
    };\
    ::simpleUnitTest::Test* const TESTCASE_NAME(test_case_name, test_name)\
        ::mTest = ::simpleUnitTest::UnitTest::getInstance()->registerTest(#test_case_name,\
        new TESTCASE_NAME(test_case_name, test_name)(#test_name));\
    void TESTCASE_NAME(test_case_name, test_name)::testBody()
    
    
#define SUTEST(test_case_name, test_name) \
        SIMPLE_TEST__(test_case_name, test_name)
    
    
//all test macro define
//#define RUN_ALL_TEST() 
    

//success: return UT_OK, else failed.
inline int RUN_ALL_TESTS()
{
    return ::simpleUnitTest::UnitTest::getInstance()->runUnitTest();
}

/***************************************************************************
 *assert macro define
 ***************************************************************************/


//implements the compare helper function
#if 0
#define IMPL_CMP_HELPER(op, expect, actual, error, onFailure) \
        if ((expect) op (actual)){\
        }else{\
            ::simpleUnitTest::UnitTest::getInstance()->getCurrentTestCase()->getCurrentTest()->setTestResult(error);\
            cout<<"["<<__FILE__<<__LINE__<<"]"<<"Error:"<<endl;\
            cout<<"Expected:("<< #expect <<") " #op " ("<< #actual <<")"<<endl;\
            cout<<"Actual:"<<(expect) << " vs " << (actual) <<endl;\
            onFailure;\
        }
#endif

#define IMPL_CMP_HELPER(cmpHelper, expect, actual, error, onFailure)\
    if (cmpHelper(#expect, #actual, error,__FILE__, __LINE__, expect, actual)){\
    }else{\
        onFailure;\
    }

#define IMPL_BOOLEAN_HELPER(expression, text, expected, actual, error, onFailure) \
    if (const ::simpleUnitTest::AssertionResult test_ar = \
        ::simpleUnitTest::AssertionResult(expression)){\
            ; \
        }else{\
            ::simpleUnitTest::UnitTest::getInstance()->getCurrentTestCase()->getCurrentTest()->setTestResult(error); \
            cout << "[" << __FILE__ << __LINE__ << "]" << "Error:" << endl; \
            cout << "Value of"<< (text) << endl; \
            cout << "Actual:"<< (actual) << endl; \
            cout << "Expected:" << (expected) << endl; \
            onFailure; \
        }

#define VOID_ONFAILURE ;
//it will be still run test function if have this failure 
#define EXPECT_IMPL_(cmpHelper, expect, actual)\
    IMPL_CMP_HELPER(cmpHelper, expect, actual, UT_FAIL, VOID_ONFAILURE)
 
//expected: expect == actual
#define EXPECT_EQ(expect, actual) EXPECT_IMPL_(simpleUnitTest::CmpHelperEQ, expect, actual)

//expected: expect != actual
#define EXPECT_NE(expect, actual) EXPECT_IMPL_(simpleUnitTest::CmpHelperNE, expect, actual)

//expected: expect <= actual
#define EXPECT_LE(expect, actual) EXPECT_IMPL_(simpleUnitTest::CmpHelperLE, expect, actual)

//expected: expect < actual
#define EXPECT_LT(expect, actual) EXPECT_IMPL_(simpleUnitTest::CmpHelperLT, expect, actual)

//expected: expect >= actual
#define EXPECT_GE(expect, actual) EXPECT_IMPL_(simpleUnitTest::CmpHelperGE, expect, actual)

//expected: expect >= actual
#define EXPECT_GT(expect, actual) EXPECT_IMPL_(simpleUnitTest::CmpHelperGT, expect, actual)

//expected: actual is true
#define EXPECT_TRUE(condition)\
    IMPL_BOOLEAN_HELPER(condition, #condition, true, false, UT_FAIL, VOID_ONFAILURE)

//expected: actual is false
#define EXPECT_FALSE(condition)\
    IMPL_BOOLEAN_HELPER(condition, #condition, false, true, UT_FAIL, VOID_ONFAILURE)

//it will be end test function immediately if occur this failure
#define ASSERT_IMPL_(cmpHelper, expect, actual)\
    IMPL_CMP_HELPER(cmpHelper, expect, actual, UT_FATAL, return)

//assert: expect == actual
#define ASSERT_EQ(expect, actual) ASSERT_IMPL_(simpleUnitTest::CmpHelperEQ, expect, actual)

//assert: expect != actual
#define ASSERT_NE(expect, actual) ASSERT_IMPL_(simpleUnitTest::CmpHelperNE, expect, actual)

//assert: expect <= actual
#define ASSERT_LE(expect, actual) ASSERT_IMPL_(simpleUnitTest::CmpHelperLE, expect, actual)

//assert: expect < actual
#define ASSERT_LT(expect, actual) ASSERT_IMPL_(simpleUnitTest::CmpHelperLT, expect, actual)

//assert: expect >= actual
#define ASSERT_GE(expect, actual) ASSERT_IMPL_(simpleUnitTest::CmpHelperGE, expect, actual)

//assert: expect >= actual
#define ASSERT_GT(expect, actual) ASSERT_IMPL_(simpleUnitTest::CmpHelperGT, expect, actual)

//assert: actual is true
#define ASSERT_TRUE(condition)\
    IMPL_BOOLEAN_HELPER(condition, #condition, true, false, UT_FATAL, return)

//assert: actual is false
#define ASSERT_FALSE(condition)\
    IMPL_BOOLEAN_HELPER(condition, #condition, false, true, UT_FATAL, return)

 #endif //end of __SIMPLE_UNIT_TEST_H__
 
